package persist

import (
	"context"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"go.uber.org/zap"
	"ylhtest01/internal/core/app"
	"ylhtest01/internal/core/outport"
)

// there is two separate requests to create table, because otherwise you would need
// to add "multiStatements=true" to MySQL connection string (and it could probably be a valid usecase).

const createContactsTableSql =
/*language=mysql*/ `
CREATE TABLE IF NOT EXISTS contacts(
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL
);
`

const createPhonesTableSql =
/*language=mysql*/ `
CREATE TABLE IF NOT EXISTS phones(
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    type TEXT NOT NULL,
    phone_number TEXT NOT NULL,
    contact_id BIGINT NOT NULL REFERENCES contacts(id)
)
`

type dbAdapter struct {
	db *sqlx.DB
}

// NewPersistence connects to MySQL database and returns Persistence interface that wraps database reference
func NewPersistence(cfg *app.Config) outport.Persistence {
	dbcfg := cfg.Database
	connStr := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%ds",
		dbcfg.User, dbcfg.Password, dbcfg.Host, dbcfg.Port, dbcfg.Name, dbcfg.ConnectTimeout)
	zap.S().Infoln("establishing connection to database...")
	db, err := sqlx.ConnectContext(context.Background(), "mysql", connStr)
	if err != nil {
		zap.S().Fatalln("error connecting to database:", err)
	}
	zap.S().Infoln("connection to database was successfully established, performing initialization...")

	tx := db.MustBegin()
	defer tx.Rollback()
	tx.MustExec(createContactsTableSql)
	tx.MustExec(createPhonesTableSql)
	err = tx.Commit()
	if err != nil {
		zap.S().Fatalln("failed to commit transaction while creating database:", err)
	}
	zap.S().Infoln("db initialization was successfully performed")

	return &dbAdapter{db: db}
}

func (d dbAdapter) DB() *sqlx.DB {
	return d.db
}

func (d dbAdapter) Close() {
	err := d.DB().Close()
	if err != nil {
		fmt.Println("failed to close mysql database:", err)
	}
}
